import { HvigorNode } from "@ohos/hvigor";
import { OhosModuleContext } from "@ohos/hvigor-ohos-plugin/src/plugin/context/plugin-context";
import RouterServiceConfig, { RouterUriConfig } from "./RouterServiceMapConfig";
import FileUtils from "../utils/FileUtils";
import { NL_ROUTER_MAP_FILE_NAME } from "./RouterConstants";
import ObjectUtils from "../utils/ObjectUtils";
import { tsProject } from "./RouterClassParser";
import { OhosPluginId } from "@ohos/hvigor-ohos-plugin";
import GeneratorStore from "./GeneratorStore";

interface GenClassExtraInfo {
  alias: string;
}

export default class RouterGenerator {
  constructor(
    private context: OhosModuleContext,
    private node: HvigorNode,
    private pluginId: string,
    private routerServiceConfig: RouterServiceConfig
  ) {}

  generator() {
    this.generatorBuildProfile();
    this.generatorRouterServiceConfigRawFile();
    this.generatorModuleUriHandler();
    this.generatorEntryConfigUriHandler();
  }

  private generatorBuildProfile() {
    const buildProfileOpt = this.context.getBuildProfileOpt();

    const runtimeOnlySources = this.routerServiceConfig.getRuntimeOnlySources();

    if (runtimeOnlySources.length) {
      let sources = ObjectUtils.ensureNestedObject(buildProfileOpt, [
        "buildOption",
        "arkOptions",
        "runtimeOnly",
        "sources",
      ]);

      if (!Array.isArray(sources)) {
        buildProfileOpt.buildOption.arkOptions.runtimeOnly.sources = [];
      }
      runtimeOnlySources.forEach((item) => {
        buildProfileOpt.buildOption.arkOptions.runtimeOnly.sources.push(item);
      });
    }

    this.context.setBuildProfileOpt(buildProfileOpt);
  }

  private generatorRouterServiceConfigRawFile() {
    const moduleJsonOpt = this.context.getModuleJsonOpt();
    const generatorRouterMap = this.routerServiceConfig.getRouterMap();

    if (!generatorRouterMap.length) {
      return;
    }

    const profileDir = `${this.node
      .getNodeDir()
      .getPath()}/src/main/resources/base/profile`;

    let routerMapJsonOpt: { routerMap: object[] };

    const routerMapFilePath: string = `${profileDir}/${NL_ROUTER_MAP_FILE_NAME}.json`;
    FileUtils.ensureFileSync(routerMapFilePath);

    if (!moduleJsonOpt.module.routerMap) {
      ObjectUtils.ensureNestedObject(moduleJsonOpt, ["module", "routerMap"]);
      routerMapJsonOpt = { routerMap: [] };
    } else {
      const routerMapFileName = moduleJsonOpt.module.routerMap.replace(
        "$profile:",
        ""
      );
      const originRouterMapFilePath = `${profileDir}/${routerMapFileName}.json`;
      routerMapJsonOpt = JSON.parse(
        FileUtils.readFileSync(originRouterMapFilePath).toString()
      );
    }

    generatorRouterMap.forEach((item) => {
      routerMapJsonOpt.routerMap.push(item);
    });

    
    FileUtils.writeFileSync(
      routerMapFilePath,
      JSON.stringify(routerMapJsonOpt)
    );

    GeneratorStore.pushFilePath(routerMapFilePath);

    moduleJsonOpt.module.routerMap = `$profile:${NL_ROUTER_MAP_FILE_NAME}`;
    this.context.setModuleJsonOpt(moduleJsonOpt);
  }

  private generatorModuleUriHandler() {
    const generatorDir = `${this.node
      .getNodeDir()
      .getPath()}/src/main/ets/generated`;
    FileUtils.ensureDirSync(generatorDir);

    const sourceFile = tsProject.createSourceFile(
      `${generatorDir}/ModuleUriHandler.ets`,
      "",
      {
        overwrite: true,
      }
    );

    const routerUris = this.routerServiceConfig.getConfig().router.routerUris;
    sourceFile.addImportDeclaration({
      moduleSpecifier: "@newlink/navigation",
      namedImports: [
        "providerNameNavigationAction",
        "RegexMatcher",
        "PathMatcher",
        "PageUriChildHandler",
        "NavParams",
        "ModuleRegisterUriHandler",
        "UriChildHandler",
        "providerUriAction",
      ],
    });

    const importNameMap = new Map<string, GenClassExtraInfo>();

    routerUris.forEach((item, index) => {
      const { path, isDefault, name } = item.imports;
      const alias = `${name}_${index}`;
      importNameMap.set(`${path}_${name}`, { alias });
      if (isDefault) {
        sourceFile.addImportDeclaration({
          moduleSpecifier: path,
          defaultImport: alias,
        });
      } else {
        sourceFile.addImportDeclaration({
          moduleSpecifier: path,
          namedImports: [`${name} as ${alias}`],
        });
      }
    });

    routerUris
      .filter((item) => item.type == "component")
      .forEach((item) => {
        const { name, path } = item.imports;
        const alias = importNameMap.get(`${path}_${name}`).alias;
        sourceFile.addStatements(
          `
@Builder
export function build${alias}(_: NavParams) {
  ${alias}();
}
            `
        );
      });

    const moduleRegisterUriHandlerCls = sourceFile.addClass({
      name: "ModuleUriHandler",
      isDefaultExport: true,
      extends: "ModuleRegisterUriHandler",
    });

    const constructorContent = routerUris
      .map((item: RouterUriConfig) => {
        const { imports, metadata, type } = item;
        const { scheme, host, path, interceptors, regex } = metadata;
        const alias = importNameMap.get(
          `${imports.path}_${imports.name}`
        ).alias;
        const matcher = regex
          ? `new RegexMatcher(${regex})`
          : `new PathMatcher({ scheme: ${
              scheme ? `"${scheme}"` : undefined
            }, host: ${host ? `"${host}"` : undefined}, path: ${
              path ? `"${path}"` : undefined
            }})`;
        const interceptorsStrArr = interceptors
          ?.map((item) => `'${item}'`)
          .join(", ");
        const interceptorsStr = `${
          interceptorsStrArr ? `[${interceptorsStrArr}]` : "[]"
        }`;

        if (type == "component") {
          return `this.addChildHandler(new PageUriChildHandler(defaultScheme, providerNameNavigationAction('${alias}' , ${matcher}, wrapBuilder(build${alias}), ${interceptorsStr})));`;
        }

        return `this.addChildHandler(new UriChildHandler(defaultScheme, providerUriAction(${matcher}, ${interceptorsStr}), new ${alias}()));`;
      })
      .join("\n");

    moduleRegisterUriHandlerCls.addConstructor({
      parameters: [{ name: "defaultScheme", type: "string" }],
      statements: `super(); 
${constructorContent}
      `,
    });

    sourceFile.saveSync();
  }

  generatorEntryConfigUriHandler() {
    if (this.pluginId !== OhosPluginId.OHOS_HAP_PLUGIN) {
      return;
    }

    const generatorDir = `${this.node
      .getNodeDir()
      .getPath()}/src/main/ets/generated`;
    FileUtils.ensureDirSync(generatorDir);

    const sourceFile = tsProject.createSourceFile(
      `${generatorDir}/EntryConfigUriHandler.ets`,
      "",
      {
        overwrite: true,
      }
    );

    const registerUriHandlerModules = Object.keys(
      this.context.getDependenciesOpt()
    )
      .filter((moduleName) => {
        const generatorModuleUriHandlerFilePath = `${this.node
          .getNodeDir()
          .getPath()}/oh_modules/${moduleName}/src/main/ets/generated/ModuleUriHandler.ets`;
        return (
          FileUtils.exist(generatorModuleUriHandlerFilePath) &&
          FileUtils.isFile(generatorModuleUriHandlerFilePath)
        );
      })
      .map((moduleName) => {
        const importModuleName =
          moduleName
            .replace("@", "")
            .replace(/[\/\-]/g, "_")
            .replace(/_./g, (match) => match.charAt(1).toUpperCase())
            .replace(/^./, (match) => match.toLowerCase())
            .replace(/^./, (match) => match.toUpperCase()) + "ModuleUriHandler";
        return {
          moduleSpecifier:
            moduleName + "/src/main/ets/generated/ModuleUriHandler",
          defaultImport: importModuleName,
        };
      });

    sourceFile.addImportDeclaration({
      moduleSpecifier: "@newlink/navigation/src/main/ets/router/UriHandlers",
      defaultImport: "ConfigUriHandler",
    });

    const mainUriHandlerModulePath = `${generatorDir}/ModuleUriHandler.ets`;
    if (FileUtils.exist(mainUriHandlerModulePath)) {
      registerUriHandlerModules.push({
        moduleSpecifier: "./ModuleUriHandler",
        defaultImport: "MainModuleUriHandler",
      });
    }

    registerUriHandlerModules?.forEach((moduleInfo) => {
      sourceFile.addImportDeclaration({
        moduleSpecifier: moduleInfo.moduleSpecifier,
        defaultImport: moduleInfo.defaultImport,
      });
    });

    const entryConfigUriHandlerCls = sourceFile.addClass({
      name: "EntryConfigUriHandler",
      isDefaultExport: true,
      extends: "ConfigUriHandler",
    });

    const constructorContent = registerUriHandlerModules
      .map((moduleInfo) => {
        return `this.addChildHandler(new ${moduleInfo.defaultImport}(defaultScheme));`;
      })
      .join("\n");

    entryConfigUriHandlerCls.addConstructor({
      parameters: [{ name: "defaultScheme", type: "string" }],
      statements: `super(); 
${constructorContent}
      `,
    });

    sourceFile.saveSync();
  }
}
